<?php
declare(strict_types=1);

namespace app\shop\library\calculate;

/**
 * 用于金额计算
 */
class Calculate
{
    private string $value;

    /**
     * @param mixed $value
     * @return Calculate
     */
    static function newDecimal(mixed $value = '0'): Calculate
    {
        return new self((string)$value);
    }

    public function __construct($value = '0')
    {
        $this->value = $this->normalize($value);
    }

    private function normalize($value): string
    {
        return strval(str_replace(',', '', (string)$value));
    }

    public function __toString()
    {
        return $this->value;
    }

    /**
     * 加法
     * @param string|float|int $num       数字/字符串或者Decimal对象
     * @param int              $precision 精度
     * @return $this
     */
    public function add(string|float|int $num, int $precision = 2): static
    {
        $this->value = bcadd($this->value, (string)$num, $precision);

        return $this;
    }

    /**
     * 减法
     * @param string|float|int $num       数字/字符串或者Decimal对象
     * @param int              $precision 小数点
     * @return $this
     */
    public function sub(string|float|int $num, int $precision = 2): static
    {
        $this->value = bcsub($this->value, (string)$num, $precision);

        return $this;
    }

    /**
     * 乘法
     * @param string|float|int $num       数字/字符串或者Decimal对象
     * @param int              $precision 小数点
     * @return $this
     */
    public function mul(string|float|int $num, int $precision = 2): static
    {
        $this->value = bcmul($this->value, (string)$num, $precision);

        return $this;
    }

    /**
     * 除法
     * @param string|float|int $num       数字/字符串或者Decimal对象
     * @param int              $precision 小数点
     * @return $this
     */
    public function div(string|float|int $num, int $precision = 2): static
    {
        $this->value = bcdiv($this->value, (string)$num, $precision);

        return $this;
    }

    /**
     * 获取计算后的数据
     */
    public function getValue(): string
    {
        return $this->value;
    }

    /**
     * 判断是否大于
     * @param string|float|int $num
     * @param int              $precision
     * @return bool
     */
    public function compGt(string|float|int $num, int $precision = 2): bool
    {
        return bccomp($this->value, (string)$num, $precision) == 1;
    }

    /**
     * 判断是否等于
     * @param string|float|int $num
     * @param int              $precision
     * @return bool
     */
    public function compEq(string|float|int $num, int $precision = 2): bool
    {
        return bccomp($this->value, (string)$num, $precision) == 0;
    }

    /**
     * 判断是否小于
     * @param string|float|int $num
     * @param int              $precision
     * @return bool
     */
    public function compLt(string|float|int $num, int $precision = 2): bool
    {
        return bccomp($this->value, (string)$num, $precision) == -1;
    }

    /**
     * 是否大于或等于
     * @param string|float|int $num
     * @param int              $precision
     * @return bool
     */
    public function compGte(string|float|int $num, int $precision = 2): bool
    {
        return bccomp($this->value, (string)$num, $precision) >= 0;
    }

    /**
     * 判断是否小于或等于
     * @param string|float|int $num
     * @param int              $precision
     * @return bool
     */
    public function compLte(string|float|int $num, int $precision = 2): bool
    {
        return bccomp($this->value, (string)$num, $precision) <= 0;
    }
}